import Cell from './cell'
import $ from 'jquery'
import ElementModel from '../model/element-model'
import ColorPicker from '../util/color-picker'
import * as cc from '../util/constants'
import {
    colorTheme
} from '../graph/theme'

/**
 * 节点基类。包含大部分公共操作
 */
class Element extends Cell {
    constructor(...args) {
        super(...args)
        this.width = cc.ELEMENT_WIDTH
        this.height = cc.ELEMENT_HEIGHT
        this.model = new ElementModel(args[0])
        this.isMinimap = args[1] && args[1].minimap
        this.isCompact = args[1] && args[1].compact
        this.jointAttrs = $.extend(true, {}, this.getBasicAttrs(), args[1])
        this.mixinAttr()
    }

    getBasicAttrs() {
        return {
            position: {
                x: this.getSvgX(),
                y: this.getSvgY()
            }, // coordinate
            size: {
                width: this.width,
                height: this.height
            },
            attrs: {}
        }
    }

    /**
     * abstract method
     */
    mixinAttr() {

    }

    getSvgX() {
        return this.model.x * (this.isMinimap ? cc.MINI_GRID_WIDTH : (this.isCompact ? cc.COMPACT_GRID_WIDTH : cc.GRID_WIDTH)) + (this.isCompact ? cc.COMPACT_GRAPH_PADDING_LEFT : cc.GRAPH_PADDING_LEFT)
    }

    getSvgY() {
        return this.model.y * (this.isMinimap ? cc.MINI_GRID_HEIGHT : (this.isCompact ? cc.COMPACT_GRID_HEIGHT : cc.GRID_HEIGHT)) + (this.isCompact ? cc.COMPACT_GRAPH_PADDING_TOP : cc.GRAPH_PADDING_TOP)
    }

    addToGraph(graph) {
        this.parent = graph
        graph.addCell(this)
        this.setColor(this.model.color)
        if (!this.isMinimap) {
            this.createMask()
            this.createText()
            this.bindEvent()
        }
    }

    bindEvent() {
        this.$mask.on('click','.ygmat-times',e => {
            this.$mask.find('.change-message').addClass('hide');
        })
        this.$mask.on('dragenter', event => {
            $(event.target).addClass('drag-target')
            let draggingElem = this.parent.draggingElement
            if (!draggingElem && this.model.type == 'input') { // 外部（input)拖入，切拖到input上，显示替换数据集选项
                this.showDropActionMenu = true
            }
            if (!this.dragging && draggingElem && draggingElem.model.type != 'output') {
                this.showDropActionMenu = true
            }
        })

        var timer
        this.$mask.on('dragleave mousedown drop', event => {
            this.$mask.removeClass('drag-target')
            timer = setTimeout(() => {
                this.showDropActionMenu = false
            }, 100)
        })

        this.$mask.on('dragenter', '.drag-action', event => {
            setTimeout(() => {
                clearTimeout(timer)
            }, 0)
        })

        this.$mask.on('dragenter', '.drag-action .action', event => {
            $(event.target).addClass('drag-target')
        })

        this.$mask.on('dragleave', '.drag-action .action', event => {
            $(event.target).removeClass('drag-target')
        })

        this.$mask.on('drop', '.drag-action', event => {
            event.stopPropagation()
            this.showDropActionMenu = false
        })

        this.$mask.on('drop', '.drag-action .join-action', event => {
            if (!this.equals(this.parent.draggingElement)) {
                this.parent.createElement(this.model, this.parent.draggingElement.model, 'join')
            }
            this.showDropActionMenu = false
        })

        this.$mask.on('drop', '.drag-action .union-action', event => {
            if (!this.equals(this.parent.draggingElement)) {
                this.parent.createElement(this.model, this.parent.draggingElement.model, 'union')
            }
            this.showDropActionMenu = false
        })

        this.$mask.on('drop', '.drag-action .link-action', event => {
            if (!this.equals(this.parent.draggingElement)) {
                let result = this.parent.createLink(this.model, this.parent.draggingElement.model, true)
                if (result) {
                    this.parent.trigger('relink_nodes', [
                        [this.parent.draggingElement, this]
                    ])
                }
            }
            this.showDropActionMenu = false
        })

        this.$mask.on('click', event => {
            this.clicked = true
            if ($(event.target).is(this.$mask)) {
                this.$mask.trigger('click_node', [this]) // 触发父节点自定义事件
            }
        })

        this.$mask.on('dblclick', event => {
            if ($(event.target).is(this.$mask)) {
                this.$mask.trigger('dblclick_node', [this])
            }
        })

        this.$mask.on('mouseleave ', event => {
            this.hovered = false
            this.showActionMenu = this.hovered
        })

        this.$mask.on('mouseenter', event => {
            this.hovered = true
            this.showActionMenu = true
        })

        this.$mask.on('dragstart', event => {
            this.showActionMenu = false
            this.dragging = true
            this.parent.draggingElement = this
        })

        this.$mask.on('dragover', event => {
            event.preventDefault()
        })

        this.$mask.on('dragend', event => {
            this.dragging = false
            this.hovered = false
            this.parent.draggingElement = null
        })

        // events for error
        this.$mask.on('mouseenter', '.element-error', event => {
            $(event.target).closest('.element-error').addClass('hover')
            this.hovered = false
            this.showActionMenu = false
        })

        this.$mask.on('mouseleave', '.element-error', event => {
            $(event.target).closest('.element-error').removeClass('hover')
        })

        // 防止在error上拖拽时，显示出错误信息
        this.$mask.on('mousedown', '.element-error', event => {
            return false
        })

        // events for filter
        this.$mask.on('mouseenter', '.element-filter', event => {
            $(event.target).closest('.element-filter').addClass('hover')
            this.hovered = false
            this.showActionMenu = false
        })

        this.$mask.on('mouseleave', '.element-filter', event => {
            $(event.target).closest('.element-filter').removeClass('hover')
        })

        // 防止在filter上拖拽时，显示出错误信息
        this.$mask.on('mousedown', '.element-filter', event => {
            return false
        })

        // events for element action
        this.$mask.on('mousedown', '.action-toggle', event => {
            this.parent.resetActionMenu()
            this.showActionMenu = true
            this.$mask.find('.action-toggle').addClass('opened').siblings('.action-menu').addClass('open')
        })

        this.$mask.on('mouseenter mouseleave', '.action-item', event => {
            $(event.target).toggleClass('hover', event.type === 'mouseenter').siblings('.action-item').removeClass('hover')
        })

        this.$mask.on('mousedown', '.action-item', event => {
            if ($(event.target).hasClass('output-action')) {
                this.parent.createElement(this.model, null, 'output')
            } else if ($(event.target).hasClass('transformer-action')) {
                this.parent.createElement(this.model, null, 'transformer')
            } else if ($(event.target).hasClass('transposer-action')) {
                this.parent.createElement(this.model, null, 'transposer')
            } else if ($(event.target).hasClass('row-transposer-action')) {
                this.parent.createElement(this.model, null, 'row-transposer')
            } else if ($(event.target).hasClass('aggregator-action')) {
                this.parent.createElement(this.model, null, 'aggregator')
            } else if ($(event.target).hasClass('insert-transformer-action')) {
                this.parent.insertElement(this.model, 'transformer')
            } else if ($(event.target).hasClass('color-action')) {
                new ColorPicker({
                    element: this.$mask,
                    theme: this.parent.theme,
                    color: this.model.color,
                    position: {
                        x: this.model.x,
                        y: this.model.y
                    },
                    change: (color, colorIndex) => {
                        this.setColor(colorIndex)
                        this.$mask.trigger('change_color', [this]) // 触发父节点自定义事件
                    }
                })
            } else if ($(event.target).hasClass('remove-action')) {
                this.remove()
            }
            this.hovered = false
            this.showActionMenu = false
        })

        if (!this.parent.readOnly) {
            // 修改名字
            this.$text.on('click', '.text:not(.readonly)', event => {
                this.editName = true
                this.parent.unhighlightLinks()
            })

            this.$text.on('keyup', '.input', event => {
                if (event.keyCode === 13) { // Enter save text
                    this.saveName()
                    this.editName = false
                } else if (event.keyCode === 27) { // ESC do not save
                    this.$text.find('.input').val(this.$text.find('.text').text())
                    this.editName = false
                }
            })

            this.$text.on('blur', '.input', event => {
                this.saveName()
                this.editName = false
            })
        }
    }

    set editName(flag) {
        this._editName = flag
        this.$text.find('.text').toggleClass('hide', flag)
        this.$text.find('.input').toggleClass('hide', !flag)
        if (flag) {
            this.$text.find('.input').focus()
        }
    }

    get editName() {
        return this._editName
    }

    remove() {
        this.jointInstance.remove()
        this.$mask.remove()
        this.$text.remove()
        this.parent.removeElement(this)
    }

    undoCreate() {
        this.jointInstance.remove()
        this.$mask.remove()
        this.$text.remove()
    }

    saveName() {
        let newText = this.$text.find('.input').val().trim()
        let oldText = this.$text.find('.text').text()
        if (newText !== oldText) { // save text
            this.$text.find('.text').text(newText).attr('title', newText)
            this.model.showName = newText
            this.$mask.trigger('change_name', [this]) // 触发父节点自定义事件
        }
    }

    // 触发事件
    trigger(eventName, params) {
        this.$mask.trigger(eventName, params)
    }

    // set Name 可用于回填
    setName(name) {
        this.model.showName = name
        this.$text.find('.input').val(name)
        this.$text.find('.text').text(name).attr('title', name)
    }

    equals(anotherElement) {
        return this.model.id === (anotherElement && anotherElement.model.id)
    }

    set showActionMenu(flag) {
        if (this.parent.readOnly) { // 图是只读时，不显示
            return
        }
        if (this._showActionMenu === flag) {
            return
        }
        this._showActionMenu = flag
        if (flag) {
            var menu = `<div class="element-action"><span class="action-toggle">${cc.ICON_PLUS}</span><ul class="action-menu">`
            if (!this.hasError) {
                menu += '<li class="action-item aggregator-action">添加聚合</li>' +
                    '<li class="action-item transposer-action">添加列转行</li>' +
                    '<li class="action-item row-transposer-action">添加行转列</li>' +
                    '<li class="action-item transformer-action">添加清理</li>'
                if (this.parent.succeedingElements(this).length > 0) {
                    menu += '<li class="action-item insert-transformer-action">插入清理</li>'
                }
                menu += '<li class="action-item output-action">添加输出</li>'
            }
            let removeHtml = this.model.editAble !== false ? '<li class="action-item remove-action">移除节点</li>' : ''
            menu += removeHtml +
                '<li class="action-item color-action">设置颜色</li>' +
                '</ul></div></div>'
            this.$mask.append(menu)
        } else {
            this.$mask.find('.element-action').remove()
            if (this.parent.succeedingElements(this).length == 0) {
                this.showActionMenu = true
            }
        }
    }

    get showActionMenu() {
        return this._showActionMenu
    }

    hasEnoughIncomingNodes() {
        return this.parent.precedingElements(this).length >= 1
    }

    /**
     * 传入前置节点id 的数组
     */
    set inComingNodes(nodeIds) {
        this.model.taskArgs.inComingNodes = nodeIds
    }

    get inComingNodes() {
        return this.model.taskArgs.inComingNodes
    }

    set showDropActionMenu(flag) {
        if (this._showDropActionMenu === flag) {
            return
        }
        this._showDropActionMenu = flag
        if (flag) {
            if (this.$mask.find('.drag-action').length == 0) {
                let relinkFlag = !this.hasEnoughIncomingNodes()
                // 已经连了线的不能再添加
                let linkedWithDraggingElem = !!this.parent.findLinkView({
                    startId: this.parent.draggingElement.id,
                    endId: this.id
                })
                // 后置节点不能连线到其前置的断线节点
                let isSucceedingElem = this.parent.isSucceedingElement(this, this.parent.draggingElement)
                // 被拖拽节点有问题则不能关联，并集
                let dragError = this.parent.draggingElement.hasError
                let relinkHtml = !dragError && relinkFlag && !linkedWithDraggingElem && !isSucceedingElem ? '<div class="action link-action">连线</div>' : ''
                let joinHtml = !this.hasError && !dragError ? '<div class="action join-action">关联</div>' : ''
                let unionHtml = !this.hasError && !dragError ? '<div class="action union-action">并集</div>' : ''
                var menu = '<div class="drag-action">' + joinHtml + unionHtml + relinkHtml + "</div>"
                this.$mask.append(menu)
                this.$text.addClass('hide')
            }
        } else {
            this.$mask.find('.drag-action').remove()
            this.$text.removeClass('hide')
        }
    }

    get showDropActionMenu() {
        return this._showDropActionMenu
    }

    set hovered(flag) {
        this._hovered = flag
        this.$mask.toggleClass('hover-target', flag)
        if (!this.clicked) {
            if (flag) {
                this._jointInstance.attr('.background', colorTheme[this.parent.theme].node.hover)
            } else {
                this._jointInstance.attr('.background', colorTheme[this.parent.theme].node.normal)
            }
        }
    }

    get hovered() {
        return this._hovered
    }

    set clicked(flag) {
        this._clicked = flag
        this.$mask.toggleClass('click-target', flag)
        if (flag) {
            this._jointInstance.attr('.background', colorTheme[this.parent.theme].node.click)
            // 高亮连线
            this.parent.unhighlightLinks()
            let connectedLines = this.parent.findLinkViews(this.id)
            for (let i in connectedLines) {
                connectedLines[i].highlight('info')
            }
        } else {
            this._jointInstance.attr('.background', colorTheme[this.parent.theme].node.normal)
            this.parent.unhighlightLinks()
        }
    }

    get clicked() {
        return this._clicked
    }

    // 适用于切换主题
    updateClicked() {
        if (this.clicked !== undefined) {
            this.clicked = this.clicked
        }
    }

    set dragging(flag) {
        this._dragging = flag
        this.$mask.toggleClass('dragging', flag)
    }

    get dragging() {
        return this._dragging
    }

    set x(x) {
        this.model.x = x
    }

    get x() {
        return this.model.x
    }

    set y(y) {
        this.model.y = y
    }

    get y() {
        return this.model.y
    }

    setColor(colorIndex) {
        if (typeof colorIndex !== 'undefined') {
            this.model.color = colorIndex
        }
    }

    getColorFromIndex(colorIndex) {
        return colorTheme[this.parent.theme].node.series[colorIndex]
    }

    /**
     * abstract method
     */
    setTaskArgs(taskArgs) {}

    /**
     * 节点移动位置
     * @param {number} deltaX 
     * @param {number} deltaY 
     */
    move(deltaX, deltaY) {
        this.model.x += deltaX
        this.model.y += deltaY
        let posX = this.getSvgX()
        let posY = this.getSvgY()
        const duration = 100

        this.jointInstance.transition('position/x', posX, {
            duration: duration,
        })
        this.jointInstance.transition('position/y', posY, {
            duration: duration,
        })

        this.$mask.animate({
            left: posX,
            top: posY
        }, duration)

        this.$text.animate({
            left: posX,
            top: posY + this.jointInstance.attributes.size.height
        }, duration)
    }

    /**
     * 设置错误标志
     * @param {string} errorMessage 
     */
    setError(errorMessage) {
        this.clearError()
        var $error = $(`<div class="element-error"><i class="ygmat-notific-error"></i><div class="error-message">${errorMessage}</div></div>`)
        this.$mask.append($error)
        if(this.$mask.find('.element-filter').length==0){
            $error.css({right:'-8px'})
        }
        // let messageH = this.$mask.find('.error-message').height()
        // this.$mask.find('.error-message').css('margin-top',`-${messageH/2+12}px`)
    }

    clearError() {
        this.$mask.find('.element-error').remove()
    }

    get hasError() {
        return this.$mask.find('.element-error').length > 0
    }

    //设置过滤标志
    setFilter(filterInfo) {
        this.clearFilter()
        let fileds = this.createFieldSpan(filterInfo)
        let $filter = $(`<div class="element-filter"><i class="fa fa-filter"></i><div class="filter-message"><div class="process_nodeFilter_title">筛选字段：</div>${fileds}</div>`)
        this.$mask.append($filter)
        // let messageH = this.$mask.find('.filter-message').height()
        // this.$mask.find('.filter-message').css('margin-top',`-${messageH/2+12}px`)
    }
    /**
     * 创建筛选字段
     * @param {Array} list 
     */
    createFieldSpan(list) {
        let fieldType = ['fa-text','fa-number','fa-calendar-o']
        let result = [];
        list.sort((a,b)=>{
            return (a.fieldShowName || '').length - (b.fieldShowName || '').length
        })
        list.forEach((item) =>{
            let filterTypeShowName = item.filterType==0?'精确筛选':(item.filterType==13 ?'高级筛选':'条件筛选')
            result.push(`<span class="element-filter-item" title="${item.fieldShowName} ${filterTypeShowName}"><i class="fa ${fieldType[(item.fieldDataType-1)]}"></i> ${item.fieldShowName} ${filterTypeShowName}</span>`)
        })
        return result.join('')
    }

    clearFilter() {
        this.$mask.find('.element-filter').remove()
    }

    //设置开始节点字段变化提示标识
    setChangeSign(changeInfo) {
        this.clearChangeSign()
        let addFields = '',deleteFields = '',changeFields = '';
        if(changeInfo.addFields && changeInfo.addFields.length>0){
            addFields = `<div class="add-field-ctx"><span class="add-field-title">新增字段：</span><span class="add-field-content">${this.createAddAndDeleteFieldSpan(changeInfo.addFields)}</span></div>`
        }
        if(changeInfo.deleteFields && changeInfo.deleteFields.length>0){
            deleteFields = `<div class="delete-field-ctx"><span class="delete-field-title">删除字段：</span><span class="delete-field-content">${this.createAddAndDeleteFieldSpan(changeInfo.deleteFields)}</span></div>`

        }

        if(changeInfo.changeSimpleFields && changeInfo.changeSimpleFields.length>0){
            changeFields = `<div  class="change-field-ctx"><span class="change-field-title">修改字段：</span><span class="change-field-content">${this.createChangeFieldSpan(changeInfo.changeSimpleFields)}</span></div>`

        }
        let $change = $(`<div class="element-change-sign"><i class="ygmat-bell-o"></i><div class="change-message"><i class="ygmat-times mat-icon"></i><div class="process_nodeChange_title">数据结构更新：</div>
        ${addFields}${deleteFields}${changeFields}
        </div>`)
        this.$mask.append($change)
        // let messageH = this.$mask.find('.change-message').height()
        // this.$mask.find('.change-message').css('margin-top',`-${messageH/2+12}px`)
        this.$mask.css('z-index',200)
    }

    createChangeFieldSpan(list){
        let fieldType = ['fa-text','fa-number','fa-calendar-o']
        let result = [];
        list.forEach((item) =>{
            result.push(`<div><span class="element-change-sign-item" title="${item.oldField.fieldShowName}"><i class="fa ${fieldType[(item.oldField.fieldType-1)]}"></i> ${item.oldField.fieldShowName}</span>&nbsp;&nbsp;&nbsp;修订为&nbsp;&nbsp;<span class="element-change-sign-item" title="${item.newField.fieldShowName}"><i class="fa ${fieldType[(item.newField.fieldType-1)]}"></i> ${item.newField.fieldShowName}</span></div>`)
        })
        return result.join('')
    }

    createAddAndDeleteFieldSpan(list){
        let fieldType = ['fa-text','fa-number','fa-calendar-o']
        let result = [];
        list.sort((a,b)=>{
            return a.fieldShowName.length - b.fieldShowName.length
        })
        list.forEach((item) =>{
            result.push(`<span class="element-add_delete-sign-item" title="${item.fieldShowName}"><i class="fa ${fieldType[(item.fieldType-1)]}"></i> ${item.fieldShowName}</span>`)
        })
        return result.join('&nbsp;&nbsp;')
    }


    

    clearChangeSign() {
        this.$mask.find('.element-change-sign').remove()
    }

    getClientPosition() {
        return {
            top: parseFloat(this.$mask.css('top')),
            left: parseFloat(this.$mask.css('left')),
            width: this.$mask.width(),
            height: this.$mask.height()
        }
    }

    /**
     * Create a transparent div over element for events
     */
    createMask() {
        var attrs = this.jointInstance.attributes
        var $mask = $(`<div class="element-mask" draggable="${!this.parent.readOnly}" for="${this.id}"></div>`)
        $mask.css({
            width: attrs.size.width - 2,
            height: attrs.size.height,
            left: attrs.position.x,
            top: attrs.position.y,
        })
        this.parent.$element.append($mask)
        this.$mask = $mask
    }

    createText() {
        var attrs = this.jointInstance.attributes
        var $text = $(`<div class="element-text" for="${this.id}"><div class="text" title="${this.model.showName}">${this.model.showName}</div><input class="input hide" type="text" value="${this.model.showName}"></div>`)
        $text.css({
            width: attrs.size.width,
            height: 20,
            left: attrs.position.x,
            top: attrs.position.y + attrs.size.height,
        })
        this.parent.$element.append($text)
        this.$text = $text
    }

    get jointInstance() {
        return this._jointInstance
    }

    get id() {
        return this.model.id
    }

    set id(id) {
        this.model.id = id
    }

}

export default Element